/*
 * 
 *  This file is part of Sourceprojects.org Lycia Copyright 2010 noctarius
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 */
package org.sourceprojects.lycia.internal.platform.base;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.xpath.XPathExpression;

import org.sourceprojects.lycia.Delegate;
import org.sourceprojects.lycia.LyciaParser;
import org.sourceprojects.lycia.TagDefinitionParser;
import org.sourceprojects.lycia.annotations.Namespace;
import org.sourceprojects.lycia.annotations.Tag;
import org.sourceprojects.lycia.exceptions.LyciaConfigurationException;
import org.sourceprojects.lycia.internal.Cache;
import org.sourceprojects.lycia.internal.InternalParserContext;
import org.sourceprojects.lycia.internal.configuration.DelegateTagDefinitionParser;
import org.sourceprojects.lycia.internal.configuration.XPathConfigurationUtils;
import org.sourceprojects.lycia.internal.platform.InternalXPathResolver;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class InternalXPathResolverImpl<B> implements InternalXPathResolver<B> {

	private final Map<XPathExpression, List<TagDefinitionParser<B>>> xpathMappings = new HashMap<XPathExpression, List<TagDefinitionParser<B>>>();
	private final LyciaParser<B> lyciaParser;

	public InternalXPathResolverImpl(final LyciaParser<B> lyciaParser) {
		this.lyciaParser = lyciaParser;
	}

	@Override
	public List<TagDefinitionParser<B>> findXPathMapping(final Element element) {
		final InternalParserContext<B> context = getInternalParserContextRepresentation();
		Cache<NodeList> xpathCache = null;

		// Try to retrieve the cache
		if (context != null) {
			xpathCache = context.getContextUtils().getXPathCache();
		}

		// Try to find mapping
		for (final XPathExpression expression : xpathMappings.keySet()) {
			if (XPathConfigurationUtils.matches(element, expression,
					xpathCache, getXmlParser())) {
				return xpathMappings.get(expression);
			}
		}

		return Collections.emptyList();
	}

	@Override
	public boolean isNamespaceInXPathMappings(final String namespaceURI) {
		final Collection<List<TagDefinitionParser<B>>> parserCollections = xpathMappings
				.values();

		// Test for namespaceURI matching
		for (final List<TagDefinitionParser<B>> parsers : parserCollections) {
			for (final TagDefinitionParser<B> parser : parsers) {
				if (parser.getNamespace() != null
						&& parser.getNamespace().equals(namespaceURI)) {
					return true;
				}
			}
		}

		// Test XPath mapping handler for namespaceURI
		for (final List<TagDefinitionParser<B>> parsers : parserCollections) {
			for (final TagDefinitionParser<B> parser : parsers) {
				if (XPathConfigurationUtils.isHandleNamespace(parser,
						namespaceURI)) {
					return true;
				}
			}
		}

		return false;
	}

	@Override
	@SuppressWarnings("unchecked")
	public void registerXPathTagDefinitionParser(final Delegate<B> delegate,
			final Tag tag, final String namespaceURI) {

		for (final Namespace namespace : tag.xpath().namespaces()) {
			checkNamespaceResolver(namespace.uri());
		}

		final XPathExpression compiled = XPathConfigurationUtils
				.compileExpression(tag.xpath().expression(), tag.xpath()
						.namespaces());

		final TagDefinitionParser<B> parser = new DelegateTagDefinitionParser<B>(
				delegate, tag, namespaceURI);
		final List<TagDefinitionParser<B>> list = asList(parser);
		xpathMappings.put(compiled, list);
	}

	private List<TagDefinitionParser<B>> asList(
			final TagDefinitionParser<B>... parsers) {
		final List<TagDefinitionParser<B>> list = new ArrayList<TagDefinitionParser<B>>();
		for (int i = 0; i < parsers.length; i++) {
			list.add(parsers[i]);
		}

		return Collections.unmodifiableList(list);
	}

	private void checkNamespaceResolver(final String namespaceURI) {
		if (getXmlParser().getResourceResolver().resolveResource(null,
				namespaceURI, null, null, null) == null) {
			throw new LyciaConfigurationException(new NullPointerException(
					"Namespace " + namespaceURI + " cannot be resolved"));
		}
	}

	private LyciaParser<B> getXmlParser() {
		return lyciaParser;
	}

	private InternalParserContext<B> getInternalParserContextRepresentation() {
		if (!(getXmlParser().getParserContext() instanceof InternalParserContext<?>)) {
			return null;
		}

		return (InternalParserContext<B>) getXmlParser().getParserContext();
	}

}
